"""
数据大屏业务服务层
负责业务逻辑处理、缓存管理和数据聚合
"""

import asyncio
from datetime import datetime
from decimal import Decimal, ROUND_HALF_UP
from typing import Optional

from app.crud.dashboard_crud import DashboardCRUD
from app.schemas.dashboard_schemas import (
	KPIMetricsResponse,
	LoadEnergyMetricsResponse,
	HighLoadStationResponse,
	StationLoadItem,
	RecentAlertsResponse,
	AlertStreamItem,
	AlarmStatsResponse,
	DashboardQueryRequest,
	HighLoadStationQueryRequest,
	RecentAlertsQueryRequest
)
from common.database import DatabaseService
from common.logger import create_logger
from common.redis import get_redis_service

logger = create_logger(__name__)


class DashboardService:
	"""数据大屏业务服务"""

	def __init__(self):
		self.redis = get_redis_service()
		self.crud = DashboardCRUD()

		# 缓存键名常量
		self.KPI_CACHE_KEY = "dashboard:kpi_metrics"
		self.LOAD_ENERGY_CACHE_KEY = "dashboard:load_energy_metrics"
		self.STATIONS_CACHE_KEY = "dashboard:high_load_stations"
		self.ALERTS_CACHE_KEY = "dashboard:recent_alerts"
		self.ALARM_STATS_CACHE_KEY = "dashboard:alarm_stats"

		# 缓存过期时间（秒）
		self.KPI_CACHE_TTL = 30  # 30秒
		self.LOAD_ENERGY_CACHE_TTL = 120  # 2分钟
		self.STATIONS_CACHE_TTL = 60  # 1分钟
		self.ALERTS_CACHE_TTL = 30  # 30秒
		self.ALARM_STATS_CACHE_TTL = 120  # 2分钟

	async def get_open_alarm_count(self, db: DatabaseService,
								   region_prefix: Optional[str] = None,
								   start_time: Optional[datetime] = None,
								   end_time: Optional[datetime] = None,
								   station_id: Optional[int] = None,
								   ) -> int:
		"""
		获取未处理告警数量

		Args:
			db: 数据库服务
			region_prefix: 区域编码前缀
			start_time: 开始时间
			end_time: 结束时间
			station_id: 台区ID

		Returns:
			int: 未处理告警数量
		"""
		return await self.crud.count_open_alarms_by_meter_codes(db, region_prefix, start_time, end_time, station_id)

	async def get_pending_work_order_count(self, db: DatabaseService,
										   region_prefix: Optional[str] = None,
										   start_time: Optional[datetime] = None,
										   end_time: Optional[datetime] = None,
										   station_id: Optional[int] = None,
										   ) -> int:
		"""
		获取未完成工单数量

		Args:
			db: 数据库服务
			region_prefix: 区域编码前缀
			start_time: 开始时间
			end_time: 结束时间
			station_id: 台区ID

		Returns:
			int: 未完成工单数量
		"""
		return await self.crud.count_pending_work_orders_by_meter_codes(db, region_prefix, start_time, end_time,
																		station_id)

	async def get_load_energy_metrics(self, db: DatabaseService,
									  request: DashboardQueryRequest) -> LoadEnergyMetricsResponse:
		"""
		获取负荷与供电量指标

		Args:
			db: 数据库服务
			request: 查询请求参数

		Returns:
			LoadEnergyMetricsResponse: 负荷与供电量指标响应
		"""
		try:
			# 构建缓存键
			cache_key_parts = [self.LOAD_ENERGY_CACHE_KEY]
			if request.region_prefix:
				cache_key_parts.append(f"region:{request.region_prefix}")
			if request.station_id:
				cache_key_parts.append(f"station:{request.station_id}")
			cache_key = ":".join(cache_key_parts)

			# 尝试从缓存获取
			if not request.force_refresh:
				cached_data = self.redis.get(cache_key)
				if cached_data:
					logger.debug(f"从缓存获取负荷与供电量数据: {cache_key}")
					return LoadEnergyMetricsResponse(**cached_data)

			logger.info("计算负荷与供电量数据")

			# 并行获取负荷、供电量、线损和分相电表数据
			load_energy_task = self.crud.get_total_load_and_energy(
				db, request.region_prefix, request.station_id
			)
			line_loss_task = self._get_line_loss_data(db, station_id=request.station_id)
			phase_meter_task = self.crud.get_phase_meter_count(db, station_id=request.station_id)

			(total_load, total_energy, load_change), line_loss_data, phase_meter_data = await asyncio.gather(
				load_energy_task, line_loss_task, phase_meter_task
			)

			# 构建响应数据
			load_energy_data = LoadEnergyMetricsResponse(
				total_load_mw=round(total_load, 2),
				total_energy_kwh=round(total_energy, 2),
				load_change_percent=round(load_change, 2),
				# 线损数据（Decimal转为float）
				total_loss_rate=float(line_loss_data.get('total_loss_rate', 0)),
				total_loss_kwh=float(line_loss_data.get('total_loss_kwh', 0)),
				# 分相电表统计
				phase_a_meter_count=phase_meter_data.get('phase_a_count', 0),
				phase_b_meter_count=phase_meter_data.get('phase_b_count', 0),
				phase_c_meter_count=phase_meter_data.get('phase_c_count', 0),
				phase_three_count=phase_meter_data.get('phase_three_count', 0),  # 三相电表数量
				updated_at=datetime.now()
			)

			# 缓存数据
			self.redis.set(
				cache_key,
				load_energy_data.model_dump(mode='json'),
				ex=self.LOAD_ENERGY_CACHE_TTL
			)
			logger.info(f"负荷与供电量数据已缓存: {cache_key}")

			return load_energy_data

		except Exception as e:
			logger.error(f"获取负载与供电量指标失败: {e}")
			return LoadEnergyMetricsResponse(
				total_load_mw=0.0,
				total_energy_kwh=0.0,
				load_change_percent=0.0,
				total_loss_rate=0.0,
				total_loss_kwh=0.0,
				phase_a_meter_count=0,
				phase_b_meter_count=0,
				phase_c_meter_count=0,
				phase_three_count=0,
				updated_at=datetime.now()
			)

	async def get_kpi_metrics(self, db: DatabaseService,
							  request: DashboardQueryRequest) -> KPIMetricsResponse:
		"""
		获取KPI指标数据（不包含负荷与供电量）

		Args:
			db: 数据库服务
			request: 查询请求参数

		Returns:
			KPIMetricsResponse: KPI指标响应
		"""
		try:
			# 构建缓存键
			cache_key_parts = [self.KPI_CACHE_KEY]
			if request.region_prefix:
				cache_key_parts.append(f"region:{request.region_prefix}")
			if request.station_id:
				cache_key_parts.append(f"station:{request.station_id}")
			if request.start_time:
				cache_key_parts.append(f"start:{request.start_time.strftime('%Y%m%d')}")
			if request.end_time:
				cache_key_parts.append(f"end:{request.end_time.strftime('%Y%m%d')}")
			cache_key = ":".join(cache_key_parts)

			# 尝试从缓存获取
			if not request.force_refresh:
				cached_data = self.redis.get(cache_key)
				if cached_data:
					logger.debug(f"从缓存获取KPI指标数据: {cache_key}")
					return KPIMetricsResponse(**cached_data)

			logger.info("计算KPI指标数据")

			device_stats_task = self.crud.get_device_status_stats(db, request.region_prefix, request.station_id)
			alarm_stats_task = self.crud.get_alarm_stats(db, request.region_prefix, request.start_time,
														 request.end_time, request.station_id)
			open_alarm_count_task = self.get_open_alarm_count(db, request.region_prefix, request.start_time,
															  request.end_time, request.station_id)
			pending_work_order_count_task = self.get_pending_work_order_count(db, request.region_prefix,
																			  request.start_time, request.end_time,
																			  request.station_id)

			device_stats, alarm_stats, open_alarm_count, pending_work_order_count = await asyncio.gather(
				device_stats_task, alarm_stats_task, open_alarm_count_task, pending_work_order_count_task
			)

			# 计算各项指标
			station_stats = device_stats.get('station', {})
			box_stats = device_stats.get('box', {})
			rtu_stats = device_stats.get('rtu', {})
			meter_stats = device_stats.get('meter', {})

			# 计算在线率
			station_online_rate = (station_stats.get('online', 0) / max(station_stats.get('total', 1), 1)) * 100
			box_online_rate = (box_stats.get('online', 0) / max(box_stats.get('total', 1), 1)) * 100
			rtu_online_rate = (rtu_stats.get('online', 0) / max(rtu_stats.get('total', 1), 1)) * 100
			meter_abnormal_rate = (meter_stats.get('abnormal', 0) / max(meter_stats.get('total', 1), 1)) * 100

			# 计算供电可靠性（简化计算，实际应根据业务规则）
			power_reliability = Decimal(99.998) - (alarm_stats.get('critical', 0) * Decimal(0.001))

			# 构建响应数据（不包含负荷与供电量）
			kpi_data = KPIMetricsResponse(
				station_total=station_stats.get('total', 0),
				station_online=station_stats.get('online', 0),
				station_online_rate=round(station_online_rate, 2),

				box_total=box_stats.get('total', 0),
				box_online=box_stats.get('online', 0),
				box_online_rate=round(box_online_rate, 2),

				rtu_total=rtu_stats.get('total', 0),
				rtu_online=rtu_stats.get('online', 0),
				rtu_online_rate=round(rtu_online_rate, 2),

				meter_abnormal=meter_stats.get('abnormal', 0),
				meter_total=meter_stats.get('total', 0),
				meter_abnormal_rate=round(meter_abnormal_rate, 2),

				alarm_total=alarm_stats.get('total', 0),
				alarm_critical=alarm_stats.get('critical', 0),
				alarm_warning=alarm_stats.get('warning', 0),
				alarm_info=alarm_stats.get('info', 0),

				open_alarm_count=open_alarm_count,
				pending_work_order_count=pending_work_order_count,

				power_reliability=power_reliability.quantize(Decimal("0.001"), rounding=ROUND_HALF_UP),

				updated_at=datetime.now()
			)

			# 缓存数据
			self.redis.set(
				cache_key,
				kpi_data.model_dump(mode='json'),
				ex=self.KPI_CACHE_TTL
			)
			logger.info(f"KPI指标数据已缓存: {cache_key}")

			return kpi_data

		except Exception as e:
			logger.error(f"获取KPI指标失败: {e}")
			return KPIMetricsResponse(
				station_total=0, station_online=0, station_online_rate=0.0,
				box_total=0, box_online=0, box_online_rate=0.0,
				rtu_total=0, rtu_online=0, rtu_online_rate=0.0,
				meter_abnormal=0, meter_total=0, meter_abnormal_rate=0.0,
				alarm_total=0, alarm_critical=0, alarm_warning=0, alarm_info=0,
				open_alarm_count=0, pending_work_order_count=0,
				power_reliability=Decimal(99.998),
				updated_at=datetime.now()
			)

	async def _get_line_loss_data(self, db: DatabaseService, station_id: Optional[int] = None) -> dict:
		"""
		获取线损数据（所有历史数据的累计）

		Args:
			db: 数据库服务
			station_id: 台区ID，用于过滤特定台区的数据

		Returns:
			dict: 线损数据
		"""
		try:
			from app.crud import line_loss_crud

			# 不传calc_date，查询所有历史数据的累计
			overview = await line_loss_crud.get_line_loss_overview(db, calc_date=None, station_id=station_id)
			return overview
		except Exception as e:
			logger.warning(f"获取线损数据失败: {e}")
			return {"total_loss_rate": 0.0, "total_loss_kwh": 0.0}

	async def get_high_load_stations(self, db: DatabaseService,
									 request: HighLoadStationQueryRequest) -> HighLoadStationResponse:
		"""
		获取高负载台区列表

		Args:
			db: 数据库服务
			request: 查询请求参数

		Returns:
			HighLoadStationResponse: 高负载台区响应
		"""
		try:
			cache_key = f"{self.STATIONS_CACHE_KEY}:{request.limit}:{request.sort_by}:{request.order}"

			# 尝试从缓存获取
			if not request.force_refresh:
				cached_data = self.redis.get(cache_key)
				if cached_data:
					logger.debug("从缓存获取高负载台区数据")
					return HighLoadStationResponse(**cached_data)

			logger.info(f"查询高负载台区数据: limit={request.limit}, sort_by={request.sort_by}, order={request.order}")

			# 从数据库获取数据
			stations_data = await self.crud.get_high_load_stations(db, request.limit, request.sort_by, request.order)

			# 转换为响应模型
			station_items = []
			for station in stations_data:
				item = StationLoadItem(
					id=station['id'],
					name=station['name'],
					code=station['code'],
					load_kw=float(station.get('load_kw', 0)),
					capacity_kva=station.get('capacity_kva'),
					load_percentage=float(station.get('load_percentage', 0)),
					alarm_count=station.get('alarm_count', 0),
					status=station.get('status', '正常'),
					status_color=station.get('status_color', 'green'),
					manager_code=station.get('manager_code'),
					updated_at=station.get('updated_at', datetime.now())
				)
				station_items.append(item)

			response_data = HighLoadStationResponse(
				stations=station_items,
				total_count=len(station_items),
				updated_at=datetime.now()
			)

			# 缓存数据
			self.redis.set(
				cache_key,
				response_data.model_dump(mode='json'),  # 所有非 JSON 类型→可序列化类型
				ex=self.KPI_CACHE_TTL
			)
			logger.info(f"高负载台区数据已缓存: {len(station_items)}条")

			return response_data

		except Exception as e:
			logger.error(f"获取高负载台区失败: {e}")
			return HighLoadStationResponse(
				stations=[], total_count=0, updated_at=datetime.now()
			)

	async def get_recent_alerts(self, db: DatabaseService,
								request: RecentAlertsQueryRequest) -> RecentAlertsResponse:
		"""
		获取最近告警列表

		Args:
			db: 数据库服务
			request: 查询请求参数

		Returns:
			RecentAlertsResponse: 最近告警响应
		"""
		try:
			cache_key = f"{self.ALERTS_CACHE_KEY}:{request.limit}"

			# 尝试从缓存获取
			if not request.force_refresh:
				cached_data = self.redis.get(cache_key)
				if cached_data:
					logger.debug("从缓存获取最近告警数据")
					return RecentAlertsResponse(**cached_data)

			logger.info(f"查询最近告警数据: limit={request.limit}")

			# 从数据库获取数据
			alerts_data = await self.crud.get_recent_alerts(db, request.limit)

			# 转换为响应模型
			alert_items = []
			for alert in alerts_data:
				item = AlertStreamItem(
					id=alert['id'],
					time=alert['time'],
					device_code=alert['device_name'],
					device_type=alert['device_type'],
					alarm_type=alert['alarm_type'],
					severity=alert['severity'],
					severity_label=alert['severity_label'],
					current_value=alert['current_value'],
					threshold=alert['threshold'],
					unit=alert['unit'],
					message=alert['message'],
					raised_at=alert['raised_at']
				)
				alert_items.append(item)

			response_data = RecentAlertsResponse(
				alerts=alert_items,
				total_count=len(alert_items),
				updated_at=datetime.now()
			)

			# 缓存数据
			self.redis.set(cache_key, response_data.model_dump(), ex=self.ALERTS_CACHE_TTL)
			logger.info(f"最近告警数据已缓存: {len(alert_items)}条")

			return response_data

		except Exception as e:
			logger.error(f"获取最近告警失败: {e}")
			return RecentAlertsResponse(
				alerts=[], total_count=0, updated_at=datetime.now()
			)

	async def get_alarm_statistics(self, db: DatabaseService,
								   force_refresh: bool = False) -> AlarmStatsResponse:
		"""
		获取告警统计数据

		Args:
			db: 数据库服务
			force_refresh: 是否强制刷新缓存

		Returns:
			AlarmStatsResponse: 告警统计响应
		"""
		try:
			# 尝试从缓存获取
			if not force_refresh:
				cached_data = self.redis.get(self.ALARM_STATS_CACHE_KEY)
				if cached_data:
					logger.debug("从缓存获取告警统计数据")
					return AlarmStatsResponse(**cached_data)

			logger.info("计算告警统计数据")

			# 并行获取各项统计数据
			alarm_stats_task = self.crud.get_alarm_stats(db)
			type_stats_task = self.crud.get_alarm_type_stats(db)
			hourly_trend_task = self.crud.get_hourly_alarm_trend(db)

			alarm_stats, type_stats, hourly_trend = await asyncio.gather(
				alarm_stats_task, type_stats_task, hourly_trend_task
			)

			response_data = AlarmStatsResponse(
				critical_count=alarm_stats.get('critical', 0),
				warning_count=alarm_stats.get('warning', 0),
				info_count=alarm_stats.get('info', 0),
				type_stats=type_stats,
				hourly_trend=hourly_trend,
				updated_at=datetime.now()
			)

			# 缓存数据
			self.redis.set(self.ALARM_STATS_CACHE_KEY, response_data.model_dump(), ex=self.ALARM_STATS_CACHE_TTL)
			logger.info("告警统计数据已缓存")

			return response_data

		except Exception as e:
			logger.error(f"获取告警统计失败: {e}")
			return AlarmStatsResponse(
				critical_count=0, warning_count=0, info_count=0,
				type_stats=[], hourly_trend=[],
				updated_at=datetime.now()
			)

	async def refresh_all_cache(self, db: DatabaseService):
		"""
		刷新所有缓存数据

		Args:
			db: 数据库服务
		"""
		try:
			logger.info("开始刷新所有缓存数据")

			# 并行刷新所有缓存，使用新的请求模型
			tasks = [
				self.get_kpi_metrics(db, DashboardQueryRequest(force_refresh=True)),
				self.get_load_energy_metrics(db, DashboardQueryRequest(force_refresh=True)),
				self.get_high_load_stations(db, HighLoadStationQueryRequest(force_refresh=True)),
				self.get_recent_alerts(db, RecentAlertsQueryRequest(force_refresh=True)),
				self.get_alarm_statistics(db, force_refresh=True)
			]

			await asyncio.gather(*tasks, return_exceptions=True)
			logger.info("所有缓存数据刷新完成")

		except Exception as e:
			logger.error(f"刷新缓存失败: {e}")

	def clear_cache(self):
		"""清除所有缓存"""
		try:
			keys_to_delete = [
				self.KPI_CACHE_KEY,
				self.ALARM_STATS_CACHE_KEY
			]

			# 删除模式匹配的键
			pattern_keys = [
				f"{self.STATIONS_CACHE_KEY}:*",
				f"{self.ALERTS_CACHE_KEY}:*"
			]

			# 删除固定键
			for key in keys_to_delete:
				self.redis.delete(key)

			# TODO: 删除模式匹配的键（需要Redis支持）
			logger.info("缓存清除完成")

		except Exception as e:
			logger.error(f"清除缓存失败: {e}")

	async def check_and_push_alarm_changes(self, db: DatabaseService):
		"""检查告警状态变化并推送WebSocket消息"""
		try:
			# 获取最近的告警状态变化
			alarm_changes = await self._get_alarm_status_changes(db)

			# 推送告警变化
			if alarm_changes:
				from app.routers.dashboard_router import broadcast_new_alert

				for change in alarm_changes:
					action = "recovered" if change["status"] == "resolved" else "new"
					await broadcast_new_alert(change["alert_data"], action=action)

				logger.info(f"推送了 {len(alarm_changes)} 个告警状态变化")

		except Exception as e:
			logger.error(f"检查告警状态变化失败: {e}")

	async def _get_alarm_status_changes(self, db: DatabaseService) -> list:
		"""获取告警状态变化"""
		try:
			# 获取Redis中存储的上次告警状态
			last_alarm_states = self.redis.get("dashboard:last_alarm_states") or {}

			# 获取当前活跃告警
			current_alarms = await self.crud.get_current_alarm_states(db)

			# 构建当前告警状态映射
			current_alarm_states = {}
			for alarm in current_alarms:
				alarm_key = f"{alarm['device_id']}_{alarm['alarm_type']}"
				current_alarm_states[alarm_key] = {
					"id": alarm["id"],
					"status": alarm["status"],
					"resolved_at": alarm.get("resolved_at")
				}

			# 检查状态变化
			changes = []

			# 检查新增告警
			for alarm_key, current_state in current_alarm_states.items():
				if alarm_key not in last_alarm_states:
					# 新告警
					alarm_data = next((a for a in current_alarms if a["id"] == current_state["id"]), None)
					if alarm_data and current_state["status"] == "active":
						changes.append({
							"type": "new",
							"status": "active",
							"alert_data": {
								"id": alarm_data["id"],
								"device_id": alarm_data["device_id"],
								"device_name": alarm_data["device_name"],
								"alarm_type": alarm_data["alarm_type"],
								"alarm_level": alarm_data["alarm_level"],
								"alarm_message": alarm_data["alarm_message"],
								"raised_at": alarm_data["raised_at"],
								"status": alarm_data["status"]
							}
						})

			# 检查告警恢复
			for alarm_key, last_state in last_alarm_states.items():
				if alarm_key in current_alarm_states:
					current_state = current_alarm_states[alarm_key]
					# 检查状态从active变为resolved
					if (last_state.get("status") == "active" and
							current_state["status"] == "resolved"):
						alarm_data = next((a for a in current_alarms if a["id"] == current_state["id"]), None)
						if alarm_data:
							changes.append({
								"type": "recovered",
								"status": "resolved",
								"alert_data": {
									"id": alarm_data["id"],
									"device_id": alarm_data["device_id"],
									"device_name": alarm_data["device_name"],
									"alarm_type": alarm_data["alarm_type"],
									"alarm_level": alarm_data["alarm_level"],
									"alarm_message": alarm_data["alarm_message"],
									"raised_at": alarm_data["raised_at"],
									"resolved_at": alarm_data.get("resolved_at"),
									"status": alarm_data["status"]
								}
							})

			# 更新Redis中的告警状态
			self.redis.set("dashboard:last_alarm_states", current_alarm_states, ex=3600)

			return changes

		except Exception as e:
			logger.error(f"获取告警状态变化失败: {e}")
			return []

	async def get_complete_alert_data(self, db: DatabaseService, alarm_id: int, existing_data: dict) -> Optional[dict]:
		"""
		获取完整的告警数据（业务逻辑层）

		Args:
			db: 数据库服务
			alarm_id: 告警ID
			existing_data: 已有数据

		Returns:
			Optional[dict]: 完整的告警数据
		"""
		try:
			# 从数据库获取告警详情
			from app.crud.alarm_crud import get_alarm_detail_by_id
			result = await get_alarm_detail_by_id(db, alarm_id)
			if not result:
				logger.warning(f"告警ID {alarm_id} 不存在")
				return None

			# 业务逻辑处理
			from datetime import datetime
			severity = result.get('severity') or 2  # 默认为一般级别
			severity_labels = {1: '提示', 2: '一般', 3: '严重'}

			# 确保 raised_at 不为空
			raised_at = result.get('raised_at') or datetime.now()

			# 构造完整的告警数据
			# 使用工具函数格式化告警消息
			from common.alarm_utils import format_alarm_message
			formatted_message = format_alarm_message(result['meter_code'], result['alarm_type'])

			complete_data = {
				'id': result['id'],
				'device_code': result['meter_code'],
				'device_type': '电表',  # 根据业务逻辑设定
				'alarm_type': result['alarm_type'],
				'severity': int(severity),
				'severity_label': severity_labels.get(int(severity), '一般'),
				'raised_at': raised_at,
				'message': result.get('rule_name') or formatted_message,  # 优先使用rule_name，其次使用格式化消息
				'time': raised_at.strftime('%H:%M:%S') if isinstance(raised_at, datetime) else None
			}

			# 合并已有数据（保留其他字段）
			for key, value in existing_data.items():
				if key not in complete_data:
					complete_data[key] = value

			return complete_data

		except Exception as e:
			logger.error(f"获取完整告警数据失败: {e}")
			return None


# 全局服务实例
dashboard_service = DashboardService()


def get_dashboard_service() -> DashboardService:
	"""获取数据大屏服务实例"""
	return dashboard_service
